// type	*       必填项11
// 		s   	:len  :min-max
// 		string  字符串
// 		number  数字
// 		ignore	非必填
// 		anyone:  与其他非必填字段必须有一个有值,号隔开
// 		postal  邮政编码6位
// 		array  数组
// 		object 对象
// 		text   中文字
// 		nottext   不能有中文字
// 		az     a到z的小写
// 		AZ     A大Z的大写
// 		phone  手机号
// 		email   邮箱
// 		www     www网址
// 		http    http的网址
// 		https   https的网址
// 		web     是网址就可以
// 		idcard  身份证
//		password  普通密码验证  以字母开头，长度在6~18之间，只能包含字母、数字和下划线
//		pluspassword 强度密码(至少包含2种字符)
//		date  日期格式
//		y   年份
//		m 	月份 12个月
//		d    日期 31天
//		H     24小时
//		h	  12小时
//		time    分秒
//		money  钱的格式
//		float  n位小数
//		qq     qq号  QQ为10000号以上
//		ip     ip地址v4
//		ip64   ipv6
//      pinyin	拼音

//		trim  去除2边空格
//		trimall 去除所有空格

/**
规则配置
[{
	autoname:'手机号',//用于自动生成错误信息
	field:'phone',//字段名
	errorClass:'error',//错误时添加的分类名 默认error  以下错误节点字段按需
	errorEl:'#username',//错误时分类添加到哪个节点
	errorTextEl:'',//错误提示内容显示到哪个节点里 默认为errorEl后面的的同级div.errtext 如果有定义则使用定义的
	errorfocusEl:'#username',//错误后聚焦元素(输入框) 默认errorEl
	rule:[{
		test:'phone',
		msg:'手机号填写错误',//指定错误信息
		max,//以下字段按需
		min,
		len,
		field
	}]//校验规则
},]
// 整理后的配置
[
    {
        "field": "name",
        "autoname": "姓名",
        "rule": [
            {
                "test": "*",
                "msg": "姓名填写错误"
            },
            {
                "test": "s",
                "max": 4,
                "min": 2,
                "msg": "姓名的长度需为2到4之间"
            }
        ]
    },
    {
        "field": "phone",
        "rule": [
            {
                "test": "phone",
                "msg": "手机号填写错误"
            }
        ]
    },
    {
        "field": "url",
        "rule": [
            {
                "test": "ignore"
            },
            {
                "test": "web",
                "msg": "网络地址长度有误，需要3-10个字"
            }
        ]
    },
    {
        "field": "email",
        "autoname": "邮箱",
        "rule": [
            {
                "test": "anyone",
                "field": [
                    "qq",
                    "wechat"
                ],
                "value": [
                    null,
                    null,
                    null
                ],
                "msg": "邮箱、QQ、微信至少需要填写一项"
            },
            {
                "test": "email",
                "msg": "邮箱格式有误"
            }
        ]
    },
]

*/
import test from './test'
const conf = {
	// debug:process.env.VITE_APP_SCHEME_DEBUG||true,//打开调试模式
	debug:true,//打开调试模式
}
// tips log
const log = function(){
	if (conf.debug) {
		console.log(...arguments);
	}
}
const err = function(){
	console.error(...arguments);
}
const warn = function(){
	console.warn(...arguments);
}
// 规则验证 通过true 不通过返回false
const getItemCheck=function(itemRg){
	let rs
	switch(itemRg.test){
		case 'array':
		case 's':
			let length = itemRg.value?.length||0
			if (itemRg.len>0) {
				rs = length==itemRg.len
			}else if(itemRg.max>0 && itemRg.min>0){
				rs = test.min(length,itemRg.min) && test.max(length,itemRg.max)
			}else if(itemRg.max>0){
				rs = test.max(length,itemRg.max)
			}else if(itemRg.min>0){
				rs = test.min(length,itemRg.min)
			}else{
				rs = test[itemRg.test](itemRg.value);
			}
		break;
		case 'min':
			rs = test.min(itemRg.value,itemRg.min)
		break;
		case 'max':
			rs = test.max(itemRg.value,itemRg.max)
		break;
		default :
			rs = test[itemRg.test](itemRg.value, itemRg.paramStr);
	}
	return rs
}
// 获取自动错误的生成
const getErrorMsg=function(autoname,rg){
	let useRule = rg.test
	switch(rg.test){
		case 's':
			if (rg.len>0) {
				useRule='sLen'
			}else if(rg.max>0 && rg.min>0){
				useRule = 'sMaxMin'
			}else if(rg.max>0){
				useRule = 'sMax'
			}else if(rg.min>0){
				useRule = 'sMin'
			}
		break;
		default :
			useRule = rg.test
	}
	const errmsg={
		s:		`请填写${autoname}`,
		sLen:	`${autoname}的长度必须为${rg.len}`,
		sMaxMin:`${autoname}的长度需为${rg.min}到${rg.max}之间'`,
		sMax:	`${autoname}的长度最多为${rg.max}`,
		sMin:	`${autoname}的长度最少为${rg.min}`,
		anyone:	`${autoname}至少需要填写一项`,
		eq:		`${autoname}输入不一致`,
		'*':	`${autoname}不能为空`,
		min:	`${autoname}最小值为${rg.min}`,
		max:	`${autoname}最大值为${rg.max}`,
		default:`${autoname}填写错误`,
	}
	return errmsg[useRule]||errmsg.default
}
//获取用户测试的规则子列表
const getTestList=function(item,value,data,fieldName){
	let itemRule=[];
	if(item.rule?.length>0){
		for (let k = 0; k < item.rule.length; k++) {
			const rg = item.rule[k];
			let itemRg = {
				...rg,
				value,
			}
			// 用于anyone、eq的value获取
			if(rg.test=='anyone' || rg.test=='eq'){
				itemRg.value=[value]
				rg.field.forEach(field=>{
					itemRg.value.push(data[field])
				})
			}
			if(item.autoname && !rg.msg){
				let msg=item.autoname
				// 特殊字段的错误生成
				if(rg.test=='anyone' || rg.test=='eq'){
					msg=[item.autoname]
					rg.field.forEach(field=>{
						msg.push(fieldName[field])
					})
					msg=msg.join('、')
				}
				// 使用autoname自动生成错误信息
				itemRg.msg = getErrorMsg(msg,rg)
			}
			itemRule.push(itemRg)
		}
	}
	return itemRule
}

// 递归取出获取所有字段名 给后面anyone用于提示
const getAutoNameForField=function(rule,prefix){
	let fieldName = {}
	rule.forEach(item=>{
		if(item.field){
			let field = prefix?prefix+'.'+item.field:item.field
			fieldName[field]=item.autoname||item.field
			if(item.children?.length>0){
				fieldName={
					...fieldName,
					...getAutoNameForField(item.children,field),
				}
			}
		}
	})
	return fieldName
}
// 递归取出所有字段的规则对应
const getRuleForField=function(rule,data,allFieldName,prefix){
	let parseRule=[]
	for (let i = 0; i < rule.length; i++) {
		let item	= rule[i]
		if(!item.field) continue;
		let field	= prefix?prefix+'.'+item.field:item.field
		let value	= data[item.field]
		let itemRule= getTestList(item,value,data,allFieldName)
		parseRule.push({
			field,
			value,
			autoname	: item.autoname,
			rule		: itemRule,
			validate	: item.validate,
			errorClass	: item.errorClass,
			errorfocusEl: validEl(item.errorfocusEl),
			errorEl		: validEl(item.errorEl),
			errorTextEl	: validEl(item.errorTextEl),
		})
		if(item.children?.length>0){
			let childRule = getRuleForField(item.children,value,allFieldName,field)
			log(childRule)
			childRule.forEach(v=>parseRule.push(v))
		}
	}
	return parseRule
}
/**
 * 检查data rule 的方法
 * @Param Object {
 * 					rule<Array>规则,
 * 					data<Object>数据,
 * 					fast<Boolean>快速模式，有错误就退出,false会爆出所有错误
 * 				}
 */
 const checkData=function({rule=[],data={},fast=false,prefix}) {
	//所有字段名
	let allFieldName = getAutoNameForField(rule,prefix)
	log('所有字段名',allFieldName)

	let parseRule	= getRuleForField(rule,data,allFieldName,prefix)
	log('所有验证规则',parseRule)
	// 如果希望所有节点能自己触发验证则需要在此处遍历规则并给予响应的事件判断

	let listRs = { status:true, msg:'验证通过',errors:[],parseRule}
	parseRule.forEach((item,key)=>{
		let itemRs={
			status:true,
			msg:'验证通过',
			errors:[],
			field:item.field,
			value:item.value,
			autoname:item.autoname,
			validate:item.validate,
			errorClass:item.errorClass,
			errorfocusEl:item.errorfocusEl,
			errorEl:item.errorEl,
			errorTextEl:item.errorTextEl,
		}
		if(item.validate){
			itemRs = item.validate(item.value)
		}else if(item.rule?.length>0){
			// 依次验证出结果 此段取到循坏外去使用parseRule遍历
			for (let k = 0; k < item.rule.length; k++) {
				let itemRg=item.rule[k]
				if(itemRg.test=='ignore'){
					// 可以忽略的情况下如果有没有值 则直接返回通过  否则进入下一个规则进行验证
					if(!itemRg.value){
						break;
					}else{
						continue;
					}
				}
				if(!test[itemRg.test]){
					warn('遇到无效规则：',itemRg);
					continue;
				}
				if(!getItemCheck(itemRg)){
					if(itemRs.status){
						itemRs.status=false
						itemRs.msg=itemRg.msg
					}
					itemRs.errors.push(itemRg.msg)
				}else{
					// anyone已通过  并且当前无值则可跳过后续规则
					if(itemRg.test=='anyone' && !item.value){
						break
					}
				}
			}
		}

		if(!itemRs.status){
			if(listRs.status){
				listRs.status=false
				listRs.msg=itemRs.msg
				listRs.autoname=itemRs.autoname
				listRs.field=itemRs.field
				listRs.value=itemRs.value
				listRs.validate=itemRs.validate
			}
			listRs.errors.push(itemRs)
		}
	})
	log('所有验证没通过的信息',listRs)

	// 错误信息显示处理 如果没有errEl元素则是视为程序不处理的 可以在check结果中遍历errors自定义处理
	createErrEl(listRs.errors)

	return listRs;
}

// 转为确定有效的el使其支持el和选择器
const validEl=function(el){
	if(!!el && typeof el =='string'){
		el=document.querySelector(el)
	}
	return el
}

// 处理错误信息  错误定位、聚焦 错误提示类和后续检测样式移除
const createErrEl=function(errList){
	errList.forEach((item,key)=>{
		let {errorClass='error',errorEl,errorTextEl,errorfocusEl=errorEl} = item
		if(!errorTextEl && errorEl){
			if(errorEl?.nextElementSibling?.classList.contains('errtext')){
				errorTextEl=errorEl.nextElementSibling
				errorTextEl.textContent=item.msg
			}
		}
		if(key==0){
			errorfocusEl?.scrollIntoView({behavior: "instant", block: "center"})
			// setTimeout(()=>{
				// 解决聚焦后的输入框弹出式内容比提示显示晚导致层级过高盖住提示的问题
				errorfocusEl?.focus();
			// },50)
		}
		// 得到焦点击后移除当前的错误样式类（或者点击也可？因为有时候并不是在一个表单元素上）
		errorEl?.classList?.add(errorClass)
		let focusFun=()=>{
			setTimeout(()=>{
				errorfocusEl?.classList.remove(errorClass)
			},1000)
			errorfocusEl?.removeEventListener('focus',focusFun)
		}
		errorfocusEl?.addEventListener('focus',focusFun)
		// 输入后移除错误提示内容
		let inputFun=()=>{
			setTimeout(()=>{
				item.msg=''
				if(errorTextEl) errorTextEl.textContent=item.msg
			},300)
			errorfocusEl?.removeEventListener('input',inputFun)
		}
		errorfocusEl?.addEventListener('input',inputFun)
	})
}

const guid =function(){
	return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
		var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
		return v.toString(16);
	});
}
// domControl
// 获取所有表单元素
 const form_init=function(form,ignoreHidden){
	let data = {}
	let rule = []
	log(form.elements)
	let forminput = form.elements;
	if(forminput){
		for (let i = 0; i < forminput.length; i++) {
			let item		= forminput[i]
			let attr		= item.attributes
			let inputType	= attr.type?.value || 'text'
			let inputName	= attr.name?.value
			// 如果已定义忽略hidden元素则遇到时跳过
			if(inputType	== 'hidden' && ignoreHidden) continue
			let attrRule	= attr.rule?.value || ''
			let msg			= attr.msg?.value || ''
			let autoname	= attr.autoname?.value || ''
			let errorClass	= attr.errorClass?.value || 'error'
			let errorEl		= attr.errorEl?.value || ''
			let errorTextEl	= attr.errorTextEl?.value || ''
			let errorfocusEl= item
			let field		= attr.field?.value || '__'+guid()
			if(attr.errorfocusEl?.value) errorfocusEl=validEl(attr.errorfocusEl.value)
			if(attrRule){
				let value = item.dataset?.hasOwnProperty('checkvalue') ? item.dataset.checkvalue: item.value
				// checkbox和radio的值需要特殊获取  此时input的name就尤为重要
				if(inputName){
					let group = form.querySelectorAll(`[name='${inputName}']`)
					log(group,inputName)
					if(inputType=='checkbox'){
						value=[]
						group.forEach(input=>{
							if(input.checked) value.push(input.value)
						})
					}else if(inputType=='radio'){
						value=''
						group.forEach(input=>{
							if(input.checked) value=input.value
						})
					}
				}
				data[field]=value
				rule.push({
					autoname,
					field,
					errorClass,
					errorEl,
					errorTextEl,
					errorfocusEl,
					rule:encodeRule(attrRule,msg),
				})
			}
		}
	}
	log({
		data,
		rule
	})
	return {
		data,
		rule
	}
}
// 解析rule与msg对应
const encodeRule = function(ruleString,msgString){
	let arr =[]
	let psRule = ruleString.split('|')
	let psMsg = msgString?.split('|')||[]
	psRule.forEach((item,i)=>{
		let test	= item;
		let msg		= psMsg[i]||''
		let max		= ''
		let min		= ''
		let len		= ''
		let field	= ''
		let paramStr   = ''
		if(item.indexOf(':')!=-1){
			let [rule,param] = item.split(':')
			test = rule
			switch(test){
				case 'array':
				case 's':
					param	= param.split('-')
					if(param.length==1){
						len=parseInt(param[0])
					}else if(param.length==2){
						param.sort()
						min = parseFloat(param[0])
						max = parseFloat(param[1])
					}
				break;
				case 'anyone':
				case 'eq':
					param	= param.split(',')
					field=param
				break;
				case 'max':
					max = parseFloat(param)
				break;
				case 'min':
					min = parseFloat(param)
				break;
				default:
					paramStr	= param
			}
		}
		arr.push({
			test,
			msg,
			max,
			min,
			len,
			field,
			paramStr
		})
	})
	return arr
}

/**
 * 表单内联式验证申明
 * @param {*} form form对象或form类
 * @param {*} onsubmit form@onsubmit事件处理
 * @param {*} ignoreHidden 校验时忽略hidden输入框
 * @returns check result
 */
const checkForm = function({form,onsubmit,ignoreHidden}){
	form = validEl(form)
	if(!form){
		err('form未找到')
		return;
	}
	if(onsubmit && typeof onsubmit != 'function'){
		err('onsubmit mast be functoin');
		return;
	}
	form.onsubmit=()=>{
		// 进行数据检查 然后返回？？？是否应在此处？ onsubmit事件待开发  先考虑一下必须的使用情景 （必须使用表单提交的场景？校验通过后使用表单提交）
		// var result = cdm.checkformdata();
		// 如果验证没通过就不进行提交的回调
		// if (!result.status) {
		// 	return false;
		// }
		// 默认使用检查结果或者onsubmit来return  否则默认返回true 即允许表单执行action动作
		if (onsubmit) {
			return onsubmit();
		}
		return true;
	}
	return checkData(form_init(form,ignoreHidden));
}



export default {checkData,checkForm,test}